﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Jarvis.Sensors
{
    class UserActivitySensor : Sensor
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WH_MOUSE_LL = 14;

        private static HookProc keyHookProc = KeyHookCallback;
        private static HookProc mouseHookProc = MouseHookCallback;

        private static IntPtr keyHookID = IntPtr.Zero;
        private static IntPtr mouseHookID = IntPtr.Zero;

        private static Boolean activity = false;

        private double activeTicks = 30;

        private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        private static IntPtr SetKeyHook(HookProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private static IntPtr SetMouseHook(HookProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        public UserActivitySensor()
            : base(1000)
        {
            this.name = "User Activity Level";
            keyHookID = SetKeyHook(keyHookProc);
            mouseHookID = SetMouseHook(mouseHookProc);
        }

        public override void Sense(Object stateInfo)
        {
            if (activity)
                activeTicks = activeTicks + 1;
            else
                activeTicks = activeTicks - 1;

            if (activeTicks < 0)
                activeTicks = 0;
            else if (activeTicks > 60)
                activeTicks = 60;

            this.log(activeTicks / 60);

            activity = false;
        }

        private static IntPtr KeyHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            activity = true;
            return CallNextHookEx(keyHookID, nCode, wParam, lParam);
        }

        private static IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            activity = true;
            return CallNextHookEx(mouseHookID, nCode, wParam, lParam);
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, 
            uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
    }
}
